﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.Iterations;

namespace SoftwareConsulting.BI11.NumericalMethods.Optimization
{
    /// <summary>
    /// Класс поиска интервала точки оптимума в одноаргументных функциях
    /// </summary>
    public class OptimalSegmentFinder : FunctionalIterator
    {
        private SuspiciousOptimalPoint[] _bestPoints;//наиболее подходящие точки
        private SuspiciousOptimalPointAndVectorFactory _pointsFactory;//фабрика точек (для разграничения цели поиска: минимум или максимум)

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="function">Функция, требующая решения</param>
        /// <param name="pointsCreator">Фабрика точек</param>
        public OptimalSegmentFinder(IOneVariableFunction function, SuspiciousOptimalPointAndVectorFactory pointsCreator)
                    : base(function)
        {
            _pointsFactory = pointsCreator;
        }

        /// <summary>
        /// Возвращает относительную погрешность
        /// </summary>
        /// <returns></returns>
        private double ComputeRelativeError()
        {
            return _bestPoints[1].BetterThan(_bestPoints[0]) && _bestPoints[1].BetterThan(_bestPoints[2])
                        ? 0 : 1;
        }

        /// <summary>
        /// Выполняет итерацию
        /// </summary>
        /// <returns>Относительная погрешность</returns>
        public override double DoIteration()
        {
            if (_bestPoints[0].BetterThan(_bestPoints[1]))
                MoveTowardNegative();
            else if (_bestPoints[2].BetterThan(_bestPoints[1]))
                MoveTowardPositive();
            return ComputeRelativeError();
        }

        /// <summary>
        /// Возвращает интервал, содержащий оптимальную точку
        /// </summary>
        public SuspiciousOptimalPoint[] BestPoints
        {
            get 
            { 
                return _bestPoints; 
            }
        }
        
        /// <summary>
        /// Создает случайную популяцию
        /// </summary>
        /// <remarks>Используется алгоритм "пьяницы"</remarks>
        public override void CreateRandomPopulation()
        {
            Random generator = new Random();
            _bestPoints = new SuspiciousOptimalPoint[3];
            if (double.IsNaN(BestZeroApproximation))
                SetBestZeroApproximation(generator.NextDouble());
            _bestPoints[0] = _pointsFactory.CreatePoint(BestZeroApproximation, Function);
            _bestPoints[1] = _pointsFactory.CreatePoint(generator.NextDouble() + _bestPoints[0].Argument, Function);
            _bestPoints[2] = _pointsFactory.CreatePoint(generator.NextDouble() + _bestPoints[1].Argument, Function);
        }

        /// <summary>
        /// Перемещает лучшие точки в сторону отрицательных
        /// </summary>
        private void MoveTowardNegative()
        {
            SuspiciousOptimalPoint newPoint = _pointsFactory.CreatePoint(3 * _bestPoints[0].Argument - 2 * _bestPoints[1].Argument, Function);
            _bestPoints[2] = _bestPoints[1];
            _bestPoints[1] = _bestPoints[0];
            _bestPoints[0] = newPoint;
        }

        /// <summary>
        /// Перемещает лучшие точки в сторону положительных
        /// </summary>
        private void MoveTowardPositive()
        {
            SuspiciousOptimalPoint newPoint = _pointsFactory.CreatePoint(3 * _bestPoints[2].Argument - 2 * _bestPoints[1].Argument, Function);
            _bestPoints[0] = _bestPoints[1];
            _bestPoints[1] = _bestPoints[2];
            _bestPoints[2] = newPoint;
        }
    }
}
